package rlm;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.agent.KnowledgeAgent;
import org.drools.agent.KnowledgeAgentConfiguration;
import org.drools.agent.KnowledgeAgentFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.definition.KnowledgePackage;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

public class KBaseManager {
	public static final String DSL_FILE = "layouts/layout1.dsl";
	public static final String[] DRL_FILES = new String[]{"layouts/placeHolders.drl", "layouts/defaults.drl"};
	public static final String[] DRL_PACKAGES = new String[]{ "../layouts/defaults.pkg"};
	
	private StatefulKnowledgeSession ksession;
	private KnowledgeBase kbase;
	private KnowledgeAgent kagent;
	private final ArrayList<String> files;
	private final Map<String, Set<String>> packageNames;
	
	public KBaseManager() throws Exception{
		files = new ArrayList<String>();
		packageNames = new TreeMap<String, Set<String>>();
		// read defaults
		
		kbase = KnowledgeBaseFactory.newKnowledgeBase();
		kbase.addKnowledgePackages(readFromClassPath(DRL_PACKAGES));
	}
	
	public KBaseManager(String[] files) throws Exception {
		this();
		for (String string : files) {
			this.files.add(string);
		}
		
		kbase.addKnowledgePackages(readFrom(files));
		agent(kbase);
	}

	public KBaseManager(File[] files2) throws Exception {
		this();
		kbase.addKnowledgePackages(readFrom(files2));
	}

	private Collection<KnowledgePackage> readFromClassPath(String ... names) throws FileNotFoundException, IOException, ClassNotFoundException{
		InputStream[] arr = new InputStream[names.length];
		int i = 0;
		for(String name : names){
			InputStream resource = this.getClass().getResourceAsStream(name);
			arr[i++] = resource;
		}
		return readFrom(arr);
	}
	
	
	private Collection<KnowledgePackage> readFrom(String ... names) throws FileNotFoundException, IOException, ClassNotFoundException{
		// when reading from files, start notifier service
		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		for(String name : names){
			Resource fileResource = ResourceFactory.newFileResource(name);
			kbuilder.add(fileResource, ResourceType.DRL);
		}
		return kbuilder.getKnowledgePackages();
	}

	public void startNotifications() {
		ResourceFactory.getResourceChangeScannerService().start();
		ResourceFactory.getResourceChangeNotifierService().start();
	}

	private Collection<KnowledgePackage> readFrom(File ... files) throws FileNotFoundException, IOException, ClassNotFoundException{
		ArrayList<KnowledgePackage> pack = new ArrayList<KnowledgePackage>();
		for(File file : files){
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
			Collection<KnowledgePackage> kpkgs = (Collection<KnowledgePackage>)in.readObject();
			in.close();
			pack.addAll(kpkgs);
		}
		return pack;
	}
	
	private Collection<KnowledgePackage> readFrom(InputStream ... files) throws IOException, ClassNotFoundException {
		ArrayList<KnowledgePackage> pack = new ArrayList<KnowledgePackage>();
		for(InputStream file : files){
			ObjectInputStream in = new ObjectInputStream(file);
			Collection<KnowledgePackage> kpkgs = (Collection<KnowledgePackage>)in.readObject();
			in.close();
			pack.addAll(kpkgs);
		}
		return pack;
	}
	
	public void update(String filename){
		//kbase.addKnowledgePackages(filename);
	}
	
	private void agent(KnowledgeBase kbase2){
		KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
		aconf.setProperty("drools.agent.newInstance", "false");
		kagent = KnowledgeAgentFactory.newKnowledgeAgent("test agent", kbase, aconf);
	}
	
	
	public StatefulKnowledgeSession getKsession() {
		return ksession;
	}
	
	public StatefulKnowledgeSession newKsession() {
		if (kagent != null)
			ksession = kagent.getKnowledgeBase().newStatefulKnowledgeSession();
		else 
			ksession = kbase.newStatefulKnowledgeSession();
		return ksession;
	}

	public void loadFile(String ... names) {
		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(kbase);
		addFilesToBuilder(kbuilder, kbase, Arrays.asList(names));
	}

	public void unleadFile(String name) {
		// 1. check if file is in the file list
			if (!files.contains(name))
				return;
			
		// 2. check whether tihs file has knowledge packages associated wih it
			Set<String> packages = packageNames.get(name);
			if (packages == null || packages.size() == 0)
				return;
		// 3. remove knowledge packages
			for(String pkg : packages)
				kbase.removeKnowledgePackage(pkg);
		// 4. update sessions
	}
	
	private void addFilesToBuilder(KnowledgeBuilder kbuilder, KnowledgeBase kbase, Collection<String> files){
		Map<String, KnowledgeBuilderErrors> allErrors = new HashMap<String, KnowledgeBuilderErrors>();
		Collection<KnowledgePackage> allKPkg = new ArrayList<KnowledgePackage>();
		
		// next, add the rest of the files
		for (String filename : files){
				kbuilder.add(ResourceFactory.newFileResource(filename), ResourceType.DRL);
				
				if (kbuilder.hasErrors()){
					allErrors.put(filename, kbuilder.getErrors());
				} else {
					// TODO:: use invoke dynamic to use scala!
					Set<String> pkg = new HashSet<String>();
					for (KnowledgePackage p : kbuilder.getKnowledgePackages())
						pkg.add(p.getName());
					packageNames.put(filename, pkg);
					allKPkg.addAll(kbuilder.getKnowledgePackages());
				}
				
				// collect the rules, remove the errors
				kbuilder.getErrors().clear();
				kbuilder.getKnowledgePackages().clear();
		}

		// add the packages, report the errors
		if (allErrors.size() > 0) {
			for (Map.Entry<String, KnowledgeBuilderErrors> errors : allErrors.entrySet()) {
				for(KnowledgeBuilderError error : errors.getValue())
					System.out.println("ERRRO: ON FILE: "+errors.getKey() +" ERROR: "+error);
			}
			// todo: if users' files are illegal, use only system files to
			// create layout
			throw new IllegalArgumentException("Could not parse knowledge.");
		}
		kbase.addKnowledgePackages(allKPkg);
	}

	/**
	 * 
	 */
	public void stopNotifications() {
		ResourceFactory.getResourceChangeScannerService().stop();
		ResourceFactory.getResourceChangeNotifierService().stop();
	}
}
